package com.store.orm.mysql;

import com.store.orm.config.MySQLConfig;
import com.store.orm.mysql.annotation.TableField;
import com.store.orm.mysql.annotation.TableName;
import com.store.orm.mysql.datasource.UnPooledConnection;
import com.store.orm.util.FieldUtil;
import com.store.orm.util.JsonUtil;
import com.store.orm.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

import static com.store.orm.common.KeyWord.*;

/**
 * @author 宫静雨
 * @version 1.0
 */
public final class MySQLUtil {
    private static final Logger logger = LoggerFactory.getLogger(MySQLUtil.class);
    private MySQLConfig mySQLConfig;

    public MySQLUtil(MySQLConfig mySQLConfig) {
        this.mySQLConfig = mySQLConfig;
    }

    /**
     * 是否开启驼峰命名法, 只用于逆向生成实体类字段的处理:
     * 数据库字段: stu_info -> 实体类字段: stuInfo
     *
     * @return 是否开启驼峰命名法
     */
    public Boolean isCamelCase() {
        return mySQLConfig.getCamelCase();
    }

    public Boolean isBatchLog() {
        return mySQLConfig.getBatchLog();
    }

    public Boolean isTruncate() {
        return mySQLConfig.getTruncate();
    }

    public Boolean isUpdateAll() {
        return mySQLConfig.getUpdateAll();
    }

    /**
     * 是否开启日志记录 SQL 功能
     *
     * @return true/false
     */
    public boolean isLogged() {
        return mySQLConfig.getLogged();
    }

    /**
     * 批处理时, 一次处理多少条数据
     *
     * @return 数据量
     */
    public int maxBatch() {
        return mySQLConfig.getMaxBatch();
    }

    public Connection getConnection() {
        UnPooledConnection unPooledConnection = new UnPooledConnection();
        return unPooledConnection.get(mySQLConfig);
    }

    /**
     * 处理值的类型问题
     */
    public void valInsertProcessor(StringBuilder sb, Object val) {
        if (val instanceof Integer || val instanceof Long || val instanceof Float || val instanceof Double
                || val instanceof Byte || val instanceof Short || val instanceof BigInteger || val instanceof BigDecimal) {
            sb.append(val).append(COMMA);
        } else if (val instanceof Date) {
            Date date = (Date) val;
            java.sql.Date time = new java.sql.Date(date.getTime());
            sb.append(SINGLE_QUOTES).append(time).append(SINGLE_QUOTES).append(COMMA);
        } else {
            sb.append(SINGLE_QUOTES).append(val).append(SINGLE_QUOTES).append(COMMA);
        }
    }

    /**
     * 处理值的类型问题
     */
    public void valDeleteProcessor(StringBuilder sb, String key, Object val) {
        if (val instanceof Integer || val instanceof Long || val instanceof Float || val instanceof Double
                || val instanceof Byte || val instanceof Short || val instanceof BigInteger || val instanceof BigDecimal) {
            sb.append(key).append(EQUAL_TO).append(val).append(AND);
        } else if (val instanceof Date) {
            Date date = (Date) val;
            java.sql.Date time = new java.sql.Date(date.getTime());
            sb.append(key).append(EQUAL_TO).append(SINGLE_QUOTES).append(time).append(SINGLE_QUOTES).append(AND);
        } else {
            sb.append(key).append(EQUAL_TO).append(SINGLE_QUOTES).append(val).append(SINGLE_QUOTES).append(AND);
        }
    }

    /**
     * 将一个 JSON 字符串解析为一个 Map 结构
     *
     * @param json 要解析的 JSON
     * @return Map
     */
    public Map<String, Object> parseJsonToMap(String json) {
        return JsonUtil.parse(json);
    }

    /**
     * 将对象解析，放入 Map 中，以便于涉及对象方法的使用
     *
     * @param t 将要被解析的对象
     * @return 返回一个 LinkedHashMap
     */
    public <T> Map<String, Object> parseTToMap(T t) {
        Class<?> cls = t.getClass();
        Field[] fields = cls.getDeclaredFields();
        Map<String, Object> map = new LinkedHashMap<>();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object v = field.get(t);
                if (Objects.nonNull(v)) {
                    map.put(getFieldName(field), v);
                }
            } catch (IllegalAccessException e) {
                logger.error("解析实体类时发生错误: {}", e.getMessage());
            }
        }
        return map;
    }

    /**
     * 根据类型获取到数据库表名
     *
     * @param cls 类名
     * @return 表名
     */
    public String getTableName(Class<?> cls) {
        String tableName;
        if (cls.isAnnotationPresent(TableName.class)) {
            TableName annotation = cls.getAnnotation(TableName.class);
            String originValue = annotation.value();
            if (!StringUtil.isBlank(originValue)) {
                tableName = originValue;
            } else {
                tableName = cls.getSimpleName();
            }
        } else {
            tableName = cls.getSimpleName();
        }
        return tableName;
    }

    /**
     * 根据 POJO字段 获取 数据库表字段的名字
     *
     * @param field POJO字段
     * @return 表字段名字
     */
    public String getFieldName(Field field) {
        String fieldName;
        if (field.isAnnotationPresent(TableField.class)) {
            TableField annotation = field.getAnnotation(TableField.class);
            String originValue = annotation.field();
            if (!StringUtil.isBlank(originValue)) {
                fieldName = originValue;
            } else {
                fieldName = field.getName();
            }
        } else {
            if (isCamelCase()) {
                fieldName = FieldUtil.pojoToMysql(field.getName());
            } else {
                fieldName = field.getName();
            }
        }
        return fieldName;
    }

    // select id,name from test
    public String prefix(String tableName, Field[] fields) {
        StringBuilder sb = new StringBuilder();
        sb.append(SELECT);
        for (Field field : fields) {
            String fieldName = getFieldName(field);
            sb.append(fieldName).append(COMMA);
        }
        sb.delete(sb.lastIndexOf(COMMA), sb.length());
        sb.append(FROM).append(tableName);
        return sb.toString();
    }
}